/* A small NCurses interface: it's really bad, nearly supports nothing,
 * Probably will be removed when there will be a GUI debugger */

#include "ncurses.h"
#include "../cpu/mips.h"
#include "../cpu/tables.h"

WINDOW *regw, *instrw, *msgw;
DbgMsg dbglog[MAX_INFMSG]; /* TODO: STATIC */
s32 lastmsg = 0;
s32 msg_cur = 0;
s32 msgy;
s32 instr_cur = 0;
s32 instry;
s32 actwin = 0;

void ncurses_init()
{
    initscr();
    start_color();
    noecho();
    curs_set(0);

    init_pair(1, COLOR_WHITE, COLOR_BLACK);

    refresh();
    regw = newwin((LINES * 2) / 3, COLS / 3, 0, (COLS * 2) / 3);
    wattron(regw, COLOR_PAIR(1));
    box(regw, 0, 0);
    wattroff(regw, COLOR_PAIR(1));
    wrefresh(regw);

    instry = (LINES * 2) / 3;
    instrw = newwin(instry, (COLS * 2) / 3, 0, 0);
    wattron(instrw, COLOR_PAIR(1));
    box(instrw, 0, 0);
    wattroff(instrw, COLOR_PAIR(1));
    wrefresh(instrw);

    msgy = LINES / 3;
    msgw = newwin(msgy, COLS, (LINES * 2) / 3, 0);
    wattron(msgw, COLOR_PAIR(1));
    box(msgw, 0, 0);
    wattroff(msgw, COLOR_PAIR(1));
    wrefresh(msgw);
    scrollok(msgw, TRUE);
    keypad(msgw, TRUE);
}

void ncurses_delay(u8 d)
{
    if (d)
        nodelay(msgw, TRUE);
    else
        nodelay(msgw, FALSE);
}

void ncurses_quit()
{
    endwin();
}

void ncurses_dbg(DbgLvl lvl, const char *format, va_list ap)
{
    u8 color;
    switch (lvl)
    {
    case ERR:
        color = COLOR_RED;
        break;
    case WRN:
        color = COLOR_YELLOW;
        break;
    default:
        color = COLOR_WHITE;
        break;
    }
    if (lastmsg == MAX_INFMSG)
    {
        s32 i;
        for (i = 0; i < MAX_INFMSG - 1; i++) {
            dbglog[i].color = dbglog[i + 1].color;
            strcpy(dbglog[i].msg, dbglog[i + 1].msg);
        }
        lastmsg--;
    }

    vsprintf(dbglog[lastmsg].msg, format, ap);

    dbglog[lastmsg].color = color;
    lastmsg++;
    msg_cur = lastmsg;
}

void ncurses_showDbg()
{
    s32 i, y;
    wclear(msgw);
    init_pair(2, COLOR_WHITE, COLOR_BLACK);
    for (i = msg_cur - msgy, y = 0; i <= msg_cur; i++, y++)
    {
        /*init_pair(2, dbglog[i].color, COLOR_BLACK); TODO: fix ? */
        wattron(msgw, COLOR_PAIR(2));
        mvwprintw(msgw, y, 1, dbglog[i].msg);
        wattroff(msgw, COLOR_PAIR(2));
    }
    wattron(msgw, COLOR_PAIR(1));
    if (actwin == 1)
        wborder(msgw, '=', '=', '=', '=', '+', '+', '+', '+');
    else
        box(msgw, 0, 0);
    wattroff(msgw, COLOR_PAIR(1));
    wrefresh(msgw);
}

void ncurses_showReg()
{
    s32 i;
    wclear(regw);
    wattron(regw, COLOR_PAIR(1));
    for (i = 0; i < 32; i++)
        mvwprintw(regw, i + 1, 1, "%s: %x\n", registers[i], cpu.r[i]);
    box(regw, 0, 0);
    wattroff(regw, COLOR_PAIR(1));
    wrefresh(regw);
}

void ncurses_showInstr()
{
    s32 i, y;
    char instr[200];
    wclear(instrw);
    wattron(instrw, COLOR_PAIR(1));
    for (i = instr_cur, y = 0; i <= instr_cur + instry; i++, y++) {
        cpu_debug(instr, 200, mem_read32_noerr(i * 4));
        mvwprintw(instrw, y, 1, "%08x [%08x]: %s", i * 4, mem_read32_noerr(i * 4), instr);
    }
    if (actwin == 0)
        wborder(instrw, '+', '+', '+', '+', '+', '+', '+', '+');
    else
        box(instrw, 0, 0);
    wattroff(instrw, COLOR_PAIR(1));
    wrefresh(instrw);
}

void ncurses_setPC(s32 pc)
{
    instr_cur = pc / 4;
}

void ncurses_show()
{
    ncurses_showReg();
    ncurses_showDbg();
    ncurses_showInstr();
}

s8 ncurses_key()
{
    switch (wgetch(msgw))
    {
    case KEY_UP:
        if (actwin == 1) {
            if (msg_cur >= msgy - 1)
                msg_cur--;
        }
        else
            instr_cur--;
        break;
    case KEY_DOWN:
        if (actwin == 1) {
            if (msg_cur < lastmsg)
                msg_cur++;
        }
        else
            instr_cur++;
        break;
    case KEY_PPAGE:
        if (actwin == 1)
        {
            if (msg_cur - msgy >= msgy - 1)
                msg_cur -= msgy;
            else
                msg_cur = msgy - 2;
        }
        else
            instr_cur -= instry;
        break;
    case KEY_NPAGE:
        if (actwin == 1)
        {
            if (msg_cur + msgy < lastmsg)
                msg_cur += msgy;
            else
                msg_cur = lastmsg;
        }
        else
            instr_cur += instry;
        break;
    case '\t':
        actwin ^= 1;
        break;
    case 'n':
        return 1;
    case 'q':
        return 0;
    case 'p':
        return 2;
    }

    return -1;
}

